package mt.dummy;

import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import mt.Order;
import mt.comm.ServerComm;
import mt.comm.ServerMessage;
import mt.server.MicroTraderServer;

public class MicroTraderServerDummy implements MicroTraderServer {

	// missing parts or wrong:
	// clients.addID
	// orders need to be different
	// bloquinkqueue

	private ServerComm serverComm;
	private BlockingQueue<Order> orders;
	// private ArrayList<ClientSideMessage> messages;
	private BlockingQueue<String> connectedClients;
	private int serverOrderID;

	/**
	 * Starts the server. Creates all the lists that are used.
	 * 
	 * @param serverComm
	 *            the object through which all communication with clients should
	 *            take place.
	 */

	@Override
	public void start(ServerComm serverComm) {
		

		orders = new LinkedBlockingQueue<Order>();
		// messages = new ArrayList<ClientSideMessage>();
		connectedClients = new LinkedBlockingQueue<String>();
		serverOrderID = 1;
		serverComm.start();
		this.serverComm = serverComm;
		 receiveMessagesFromServer();

	}

	private void receiveMessagesFromServer() {

		while (true) {

			if (serverComm.hasNextMessage()) {
				System.out.println("la vai uma ");

				ServerMessage msg = (ServerMessage) serverComm.getNextMessage();

				switch (msg.getType()) {

				case CONNECTED:

					 clientConnect(msg.getSenderNickname());
					System.out.println("ola eu recebi o cliente: "
							+ msg.getSenderNickname());
					break;

				 case NEW_ORDER:
				 addOrder(msg.getOrder());
				 System.out.println("---->>");
				
				 break;
				
				 case DISCONNECTED:
				 clientDisconnect(msg.getSenderNickname());
				 break;

				default:
					System.out.println("Message type Unknown");
					break;

				}

			}
		}

	}

	/**
	 * Adds a order to the orders list that is created in the start method.
	 * Informs all the clients that a new order was created.
	 * 
	 * @param order
	 *            the object that is added to the list.
	 */

	public void addOrder(Order order) {

		// cannot be orders.size because if an element is deleted an order get
		// an equal id
		// order.setServerOrderID(orders.size());

		order.setServerOrderID(serverOrderID);
		serverOrderID++;
		orders.add(order);

		// for each client update list of orders
		// for (String string : connectedClients) {
		// System.out.println("+++++ " + string + " ++++ +");
		// serverComm.sendOrder(string, order);
		// }

		//updateClients();
		updateClientsAboutNewOrder(order);
		
		if (order.isBuyOrder()) {
			satisfactionOrderSell(order);
		} else if (order.isSellOrder()) {
			satisfactionOrderBuy(order);
		}
	}

	/**
	 * Go to the orders list and see if exist one order with the same stock
	 * name, if exists the buy order can't be complete or parcial satisfied
	 * 
	 * @param buy
	 *            Order to be compared with all the sell Orders on the orders
	 *            list.
	 * 
	 * 
	 */

	public void satisfactionOrderSell(Order buyOrder) {
		
		for (Order sellOrder : orders) {

			// If sellOrder is a SellOrder
			if (sellOrder.isSellOrder()) {

				// If sellOrder have a same name of buyOrder
				if (sellOrder.getStock().equals(buyOrder.getStock())) {

					// If buyOrder price is >= sellOrder price
					if (buyOrder.getPricePerUnit() >= sellOrder
							.getPricePerUnit()) {

						// Number of units buyOrder < number of units of
						// sellOrder ---> buyOrder parcialmente satisfeita
						// Number of units buyOrder >= number of units of
						// sellOrder ---> buyOrder completamente satisfeita
						if (buyOrder.getNumberOfUnits() < sellOrder
								.getNumberOfUnits()) {
							int units = 0;
							units = sellOrder.getNumberOfUnits()
									- buyOrder.getNumberOfUnits();
							sellOrder.setNumberOfUnits(units);
							updateClients();

							System.out.println("Parcial satisfaction order "
									+ buyOrder.toString());
							orders.remove(buyOrder);
						} else if (buyOrder.getNumberOfUnits() > sellOrder
								.getNumberOfUnits()) {
							int units = 0;
							units = buyOrder.getNumberOfUnits()
									- sellOrder.getNumberOfUnits();
							buyOrder.setNumberOfUnits(units);
							updateClients();

							System.out.println("Complete satisfaction order "
									+ sellOrder.toString());
							orders.remove(sellOrder);
						} else if (buyOrder.getNumberOfUnits() == sellOrder
								.getNumberOfUnits()) {
							buyOrder.setNumberOfUnits(0);
							sellOrder.setNumberOfUnits(0);
							//updateClients();

							System.out
									.println("Complete satisfaction orders BUYORDER: "
											+ buyOrder.toString()
											+ " /nSELLORDER: "
											+ sellOrder.toString());
							orders.remove(sellOrder);
							orders.remove(buyOrder);
							
						}
					}
				}
			}

		}
	}

	/**
	 * Go to the orders list and see if exist one order with the same stock
	 * name, if exists the sell order can be complete or parcial satisfied
	 * 
	 * @param sell
	 *            Order to be compared with all the buy Orders on the orders
	 *            list.
	 * 
	 * 
	 */
	public void satisfactionOrderBuy(Order sellOrder) {

		for (Order buyOrder : orders) {

			// Verify if buyOrder is a BuyOrder
			if (buyOrder.isBuyOrder()) {

				// Verify if stock name of buyOrder is equals to sellOrder stock
				// name
				if (buyOrder.getStock().equals(sellOrder.getStock())) {

					// If price of buyer is <= seller
					if (sellOrder.getPricePerUnit() >= buyOrder
							.getNumberOfUnits()) {

						// Number of units sellOrder < number of units of
						// buyOrder ---> sellOrder parcialmente satisfeita
						// Number of units sellOrder >= number of units of
						// buyOrder ---> sellOrder completamente satisfeita
						if (sellOrder.getNumberOfUnits() < buyOrder
								.getNumberOfUnits()) {
							int units = 0;
							units = buyOrder.getNumberOfUnits()
									- sellOrder.getNumberOfUnits();
							buyOrder.setNumberOfUnits(units);
							updateClients();

							System.out.println("Parcial satisfaction order "
									+ sellOrder.toString());
							orders.remove(sellOrder);

						} else if (sellOrder.getNumberOfUnits() > buyOrder
								.getNumberOfUnits()) {
							int units = 0;
							units = sellOrder.getNumberOfUnits()
									- buyOrder.getNumberOfUnits();
							sellOrder.setNumberOfUnits(units);
							updateClients();

							System.out.println("Complete satisfaction order "
									+ buyOrder.toString());
							orders.remove(buyOrder);

						} else if (sellOrder.getNumberOfUnits() == buyOrder
								.getNumberOfUnits()) {
							
							//updateClients();

							System.out
									.println("Complete satisfaction orders BUYORDER: "
											+ buyOrder.toString()
											+ " /nSELLORDER: "
											+ sellOrder.toString());
							orders.remove(sellOrder);
							orders.remove(buyOrder);
						}
					}
				}
			}

		}
	}

	/**
	 * Connects a new client.Sends all orders to the client.
	 * 
	 * @param nickname
	 *            String with the user nickname.
	 * 
	 */

	public void clientConnect(String nickname) {
		// add client to a list
		if (serverComm.clientIsConnected(nickname)) {

			connectedClients.add(nickname);

			// send all orders pending to him
			for (Order order : orders) {
				serverComm.sendOrder(nickname, order);
			}
		}
	}

	/**
	 * 
	 * Disconnects a client, removing all orders containing its nickname.
	 * 
	 * 
	 * @param nickname
	 *            String with the user nickname.
	 */

	public void clientDisconnect(String nickname) {

		if (!orders.isEmpty()) {
			// delete orders from this client

			for (Order order : orders) {

				if (order.getNickname().equals(nickname)) {

					System.out
							.println("Como me desliguei ir apagar esta ordem: "
									+ order.getStock());

					// this makes sense ?

					// order.setNumberOfUnits(0);
					// messages.add(new Message(ClientSideMessage.Type.ORDER,
					// order));

					orders.remove(order);

				}

			}
			connectedClients.remove(nickname);
			updateClients();
			// serverComm.disconnectClient(nickname);

		}
	}
	
	private void updateClientsAboutNewOrder(Order order){
		for(String client : connectedClients){
			serverComm.sendOrder(client, order);
		}
	}

	private void updateClients() {

		// go through all clients, and for each one send all orders
		// check repeat messages.

		for (String client : connectedClients) {

			System.out.println("clientes connectados: " + client);
			for (Order order : orders) {
				serverComm.sendOrder(client, order);
			}
		}
	}

}
